package pool

import (
	"github.com/go-redis/redis"
	"github.com/pkg/errors"
	"litter-api/app/conf"
	"time"
)

var Redis Cache
var _ Cache = (*cacheRepo)(nil)

type Cache interface {
	i()
	Set(key, value string, ttl time.Duration) error
	SetNX(key, value string, ttl time.Duration) (bool, error)
	Get(key string) (string, error)
	TTL(key string) (time.Duration, error)
	Expire(key string, ttl time.Duration) bool
	ExpireAt(key string, ttl time.Time) bool
	Del(key string) bool
	Exists(keys ...string) bool
	Incr(key string) int64
	TimeUnixNano() int64
	Close() error
	LPush(key string, value string) error
}

type cacheRepo struct {
	client *redis.Client
}

func (c *cacheRepo) SetNX(lockKey, value string, ttl time.Duration) (bool, error) {
	resp := c.client.SetNX(lockKey, value, ttl)
	lockSuccess, err := resp.Result()
	return lockSuccess, err
}

func NewCache() (Cache, error) {
	client, err := redisConnect()
	if err != nil {
		return nil, err
	}

	Redis = &cacheRepo{
		client: client,
	}

	return Redis, nil
}

func (c *cacheRepo) i() {}

func redisConnect() (*redis.Client, error) {
	cfg := conf.GetConf().Redis.Mms
	client := redis.NewClient(&redis.Options{
		Addr:         cfg.Addr,
		Password:     cfg.Pass,
		DB:           cfg.Db,
		MaxRetries:   cfg.MaxRetries,
		PoolSize:     cfg.PoolSize,
		MinIdleConns: cfg.MinIdleConns,
	})

	if err := client.Ping().Err(); err != nil {
		return nil, err
	}

	return client, nil
}

// Set set some <key,value> into redis
func (c *cacheRepo) Set(key, value string, ttl time.Duration) error {
	if err := c.client.Set(key, value, ttl).Err(); err != nil {
		return errors.Wrapf(err, "redis set key: %s err", key)
	}

	return nil
}

// Get get some key from redis
func (c *cacheRepo) Get(key string) (string, error) {
	value, err := c.client.Get(key).Result()
	if err != nil {
		return "", errors.Wrapf(err, "redis get key: %s err", key)
	}

	return value, nil
}

// LPush some string into redis list
func (c *cacheRepo) LPush(key string, values string) error {
	if err := c.client.LPush(key, values).Err(); err != nil {
		return errors.Wrapf(err, "redis lpush key: %s err", key)
	}

	return nil
}

// TTL get some key from redis
func (c *cacheRepo) TTL(key string) (time.Duration, error) {
	ttl, err := c.client.TTL(key).Result()
	if err != nil {
		return -1, errors.Wrapf(err, "redis get key: %s err", key)
	}

	return ttl, nil
}

// Expire expire some key
func (c *cacheRepo) Expire(key string, ttl time.Duration) bool {
	ok, _ := c.client.Expire(key, ttl).Result()
	return ok
}

// ExpireAt expire some key at some time
func (c *cacheRepo) ExpireAt(key string, ttl time.Time) bool {
	ok, _ := c.client.ExpireAt(key, ttl).Result()
	return ok
}

func (c *cacheRepo) Exists(keys ...string) bool {
	if len(keys) == 0 {
		return true
	}
	value, _ := c.client.Exists(keys...).Result()
	return value > 0
}

func (c *cacheRepo) Del(key string) bool {
	if key == "" {
		return true
	}

	value, _ := c.client.Del(key).Result()
	return value > 0
}

func (c *cacheRepo) Incr(key string) int64 {
	value, _ := c.client.Incr(key).Result()
	return value
}

func (c *cacheRepo) TimeUnixNano() int64 {
	value := c.client.Time().Val().UnixNano()
	return value
}

// Close close redis client
func (c *cacheRepo) Close() error {
	return c.client.Close()
}
